package ssl;

import org.apache.commons.codec.digest.DigestUtils;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.OtherName;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.security.Key;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Enumeration;

/**
 * 证书验证
 */
public class CertificateValidator {

    static {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }

    public static boolean validateServerBinding(KeyStore keyStore,
                                                String alias,
                                                String password) throws Exception {
        // 1. 基本验证
        Key key = keyStore.getKey(alias, password.toCharArray());
        if (key == null) {
            throw new SecurityException("无法获取私钥 - 密码错误或别名不存在");
        }

        X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
        cert.checkValidity();
        cert.verify(cert.getPublicKey());

        // 2. 转换为Bouncy Castle对象
        X509CertificateHolder certHolder = new X509CertificateHolder(cert.getEncoded());

        // 3. 获取扩展（新API方式）
        Extension extension = certHolder.getExtension(Extension.subjectAlternativeName);
        if (extension == null) {
            throw new SecurityException("证书缺少服务器绑定扩展");
        }

        // 4. 解析扩展值
        GeneralNames gns = GeneralNames.getInstance(extension.getParsedValue());
        String currentServerId = getCurrentServerId();

        // 5. 验证服务器ID
        for (GeneralName gn : gns.getNames()) {
            if (gn.getTagNo() == GeneralName.otherName) {
                OtherName otherName = OtherName.getInstance(gn.getName());
                try {
                    // 尝试多种方式获取字符串值
                    String certServerId;
                    if (otherName.getValue() instanceof DERUTF8String) {
                        certServerId = ((DERUTF8String)otherName.getValue()).getString();
                    } else if (otherName.getValue() instanceof ASN1String) {
                        certServerId = ((ASN1String)otherName.getValue()).getString();
                    } else {
                        certServerId = otherName.getValue().toString();
                    }
                    certServerId = certServerId.replaceAll("[^a-zA-Z0-9]", "");

                    if (!certServerId.equals(currentServerId)) {
                        throw new SecurityException(String.format(
                                "服务器绑定验证失败\n证书绑定ID: %s\n当前服务器ID: %s",
                                certServerId, currentServerId));
                    }
                    return true;
                } catch (Exception e) {
                    throw new SecurityException("解析服务器ID失败", e);
                }
            }
        }

        throw new SecurityException("未找到有效的服务器绑定信息");
    }

    private static String getCurrentServerId() throws Exception {
        // 组合多个硬件信息作为唯一标识
        String hostName = InetAddress.getLocalHost().getHostName();

        // 修改: 遍历所有网络接口，找到第一个有效的MAC地址
        byte[] macBytes = null;
        Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
        while (networkInterfaces.hasMoreElements() && macBytes == null) {
            NetworkInterface ni = networkInterfaces.nextElement();
            if (ni.isUp() && !ni.isLoopback() && ni.getHardwareAddress() != null) {
                macBytes = ni.getHardwareAddress();
            }
        }

        // 如果没有找到有效的MAC地址，使用主机名的哈希值
        if (macBytes == null) {
            System.out.println("警告: 无法获取有效的MAC地址，使用主机名的哈希值作为替代");
            macBytes = DigestUtils.sha256(hostName.getBytes());
        }

        StringBuilder macAddress = new StringBuilder();
        for (byte b : macBytes) {
            macAddress.append(String.format("%02X", b));
        }
        return DigestUtils.sha256Hex(hostName + macAddress).replaceAll("[^a-zA-Z0-9]", ""); // 清理不可见字符
    }
}